﻿using MongoDB.Driver;
using Sustainalytics.BusinessResearchProductStates.Entities.ResearchState;
using Sustainalytics.Synchronization.Interface;
using Sustainalytics.ClientDW.Synchronization.SyncRiskRatingsData.CorporateDataLogic;
using Sustainalytics.ClientDW.Synchronization.SyncRiskRatingsData.Events;
using Sustainalytics.ClientDW.Synchronization.SyncRiskRatingsData.Helpers;
using Sustainalytics.ClientDW.Synchronization.SyncRiskRatingsData.Indexes;
using Sustainalytics.ClientDW.Synchronization.SyncRiskRatingsData.RiskRatingClusters;
using Sustainalytics.ClientDW.Synchronization.SyncRiskRatingsData.RiskRatingResearchProfileLogic;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Sustainalytics.Entities.ConnectionStrings;

namespace Sustainalytics.ClientDW.Synchronization.SyncRiskRatingsData
{
    public class RiskRatingsSync
    {
        private readonly HBaseQuery _hbaseQuery;
        private readonly RiskRatingResearchQuery _riskRatingsResearchQuery;
        private readonly RiskRatingResearchProfileQuery _riskRatingsResearchProfileQuery;
        private readonly ICorporateDataProcessor _corporateDataProcessor;
        private readonly SyncCommand _syncCommand;
        private readonly ILogger _logger;
        private readonly EventsQuery _eventsQuery;
        private readonly IncidentsQuery _incidentQuery;
        private readonly GovernancePillarsHelper _governancePillarsHelper;
        private readonly MaterialEsgIssueTypeComparer meiTypeComparer = new MaterialEsgIssueTypeComparer();

        public RiskRatingsSync(
            HBaseQuery hbaseQuery,
            RiskRatingResearchQuery riskRatingsResearchQuery,
            RiskRatingResearchProfileQuery riskRatingResearchProfileQuery,
            EventsQuery eventsQuery,
            IncidentsQuery incidentQuery,
            CorporateDataProcessor corporateDataProcessor,
            SyncCommand syncCommand,
            ILogger logger,
            GovernancePillarsHelper governancePillarHelper)
        {
            _hbaseQuery = hbaseQuery;
            _syncCommand = syncCommand;
            _riskRatingsResearchQuery = riskRatingsResearchQuery;
            _riskRatingsResearchProfileQuery = riskRatingResearchProfileQuery;
            _corporateDataProcessor = corporateDataProcessor;
            _logger = logger;
            _eventsQuery = eventsQuery;
            _incidentQuery = incidentQuery;
            _governancePillarsHelper = governancePillarHelper;
        }

        public async Task<int> StartAsync(bool dropCollection = true)
        {
            var stopWatch = Stopwatch.StartNew();
            var sharedCorporateDetails = _hbaseQuery.Get(RiskRatingCorporateDataQuery.Get).ToDictionary(x => x.CompanyId, x => x);
            var companyIds = sharedCorporateDetails.Keys.ToList();
            var sharedRiskRatingClusters = _riskRatingsResearchQuery.Get(RiskRatingClusterQuery.Get);
            var sharedRiskRatingManageableClusters = _riskRatingsResearchQuery.Get(RiskRatingManageableClusterQuery.Get);
            var sharedRiskRatingManagementClusters = _riskRatingsResearchQuery.Get(RiskRatingManagementClusterQuery.Get);
            var sharedRiskRatingExposureClusters = _riskRatingsResearchQuery.Get(RiskRatingExposureClusterQuery.Get);
            var sharedRiskRatingResearchProfiles = _riskRatingsResearchProfileQuery.Get(companyIds);
            var newsevents = _eventsQuery.Get(companyIds);
            var incidents = _incidentQuery.Get(companyIds);
            var mapping = await _eventsQuery.GetMapping();


            if (sharedRiskRatingResearchProfiles.Count == 0) return 0;

            _logger.Log(
                $"{sharedRiskRatingResearchProfiles.Count} risk rating research profile for " +
                $"{new StackFrame(1).GetMethod().Name} in " +
                $"{typeof(CompanyProfileResearchState).Name} (duration {stopWatch.Elapsed}");

            OrderRiskRatingsResearchProfiles(sharedRiskRatingResearchProfiles);

            var riskRatingResearchProfiles = RiskRatingResearchProfileMapping.Map(sharedCorporateDetails, sharedRiskRatingResearchProfiles);
            var materialEsgIssues = RiskRatingMaterialEsgIssueMapping.Map(sharedCorporateDetails, sharedRiskRatingResearchProfiles);
            var riskRatingIndicators = RiskRatingIndicatorMapping.Map(sharedRiskRatingResearchProfiles);
            var riskRatingClusters = RiskRatingClusterMapping.Map(sharedRiskRatingClusters);
            var manageableClusters = RiskRatingClusterMapping.Map(sharedRiskRatingManageableClusters);
            var managementClusters = RiskRatingClusterMapping.Map(sharedRiskRatingManagementClusters);
            var exposureClusters = RiskRatingClusterMapping.Map(sharedRiskRatingExposureClusters);
            var events = RiskRatingEventsMapping.Map(newsevents, mapping);
            var sources = Sources.RiskRatingIndicatorSource.Map(riskRatingIndicators);
            var governancePillars = _governancePillarsHelper.Get(companyIds, materialEsgIssues);
            await Task.WhenAll(new[]
        {
                _syncCommand.ExecuteAsync(riskRatingResearchProfiles, RiskRatingResearchProfileIndexes.RecreateIndexes),
                _syncCommand.ExecuteAsync(materialEsgIssues, RiskRatingMaterialEsgIssueIndexes.RecreateIndexes),
                _syncCommand.ExecuteAsync(riskRatingIndicators, RiskRatingIndicatorIndexes.RecreateIndexes),
                _syncCommand.ExecuteAsync(riskRatingClusters, RiskRatingClusterIndexes.RecreateIndexes),
                _syncCommand.ExecuteAsync(manageableClusters, RiskRatingClusterIndexes.RecreateIndexes),
                _syncCommand.ExecuteAsync(managementClusters, RiskRatingClusterIndexes.RecreateIndexes),
                _syncCommand.ExecuteAsync(exposureClusters, RiskRatingClusterIndexes.RecreateIndexes),
                _syncCommand.ExecuteAsync(events, RiskRatingsEventsIndexes.RecreateIndexes, dropCollection),
                _syncCommand.ExecuteAsync(incidents, RiskRatingsIncidentsIndexes.RecreateIndexes, dropCollection),
                _syncCommand.ExecuteAsync(sources, null, dropCollection),
                _syncCommand.ExecuteAsync(governancePillars, RiskRatingsGovernancePillarsIndexes.RecreateIndexes)
            }).ConfigureAwait(false);

            await _corporateDataProcessor.SetResearchInProgress(sharedCorporateDetails.Keys.Where(cId => sharedRiskRatingResearchProfiles.All(rp => rp.Id != cId)));
            return sharedRiskRatingResearchProfiles.Count;
        }

        public void OrderRiskRatingsResearchProfiles(IList<SharedEntities.RiskRatingsResearch.ComprehensiveRiskRatingResearchProfile> sharedRiskRatingResearchProfiles)
        {
            Parallel.ForEach(sharedRiskRatingResearchProfiles, new ParallelOptions { MaxDegreeOfParallelism = 6 }, profile =>
            {
                profile.MaterialEsgIssues = profile.MaterialEsgIssues.OrderBy(x => x.Type.Value, meiTypeComparer).ThenByDescending(x => x.FinalWeight.Value).ToList();

                for (int sortKey = 0; sortKey < profile.MaterialEsgIssues.Count(); sortKey++)
                {
                    var materialEsgIssue = profile.MaterialEsgIssues.Skip(sortKey).Take(1).First();

                    profile.MaterialEsgIssues.Single(x => x.ExternalId == materialEsgIssue.ExternalId).SortKey.Value = sortKey;
                }
            });
        }
    }
    
    public class MaterialEsgIssueTypeComparer : IComparer<SharedEntities.RiskRatingsResearch.MaterialIssueType>
    {
        public int Compare(
            SharedEntities.RiskRatingsResearch.MaterialIssueType x, 
            SharedEntities.RiskRatingsResearch.MaterialIssueType y)
        {
            if (x == SharedEntities.RiskRatingsResearch.MaterialIssueType.BaselineIssue && y != SharedEntities.RiskRatingsResearch.MaterialIssueType.BaselineIssue)
                return -1;

            if (x != SharedEntities.RiskRatingsResearch.MaterialIssueType.BaselineIssue && y == SharedEntities.RiskRatingsResearch.MaterialIssueType.BaselineIssue)
                return 1;

            if (x == y)
                return 0;

            return 0;
        }
    }
}
